home *** CD-ROM | disk | FTP | other *** search
/ NOVA - For the NeXT Workstation / NOVA - For the NeXT Workstation.iso / SourceCode / AdobeExamples / NX_ImportAdv / GraphicImport.m < prev    next >
Encoding:
Text File  |  1992-12-19  |  22.7 KB  |  1,023 lines

  1.  
  2. /*
  3.  * (a)  (C) 1990 by Adobe Systems Incorporated. All rights reserved.
  4.  *
  5.  * (b)  If this Sample Code is distributed as part of the Display PostScript
  6.  *    System Software Development Kit from Adobe Systems Incorporated,
  7.  *    then this copy is designated as Development Software and its use is
  8.  *    subject to the terms of the License Agreement attached to such Kit.
  9.  *
  10.  * (c)  If this Sample Code is distributed independently, then the following
  11.  *    terms apply:
  12.  *
  13.  * (d)  This file may be freely copied and redistributed as long as:
  14.  *    1) Parts (a), (d), (e) and (f) continue to be included in the file,
  15.  *    2) If the file has been modified in any way, a notice of such
  16.  *      modification is conspicuously indicated.
  17.  *
  18.  * (e)  PostScript, Display PostScript, and Adobe are registered trademarks of
  19.  *    Adobe Systems Incorporated.
  20.  * 
  21.  * (f) THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO
  22.  *    CHANGE WITHOUT NOTICE, AND SHOULD NOT BE CONSTRUED
  23.  *    AS A COMMITMENT BY ADOBE SYSTEMS INCORPORATED.
  24.  *    ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY
  25.  *    OR LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO
  26.  *    WARRANTY OF ANY KIND (EXPRESS, IMPLIED OR STATUTORY)
  27.  *    WITH RESPECT TO THIS INFORMATION, AND EXPRESSLY
  28.  *    DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY, 
  29.  *    FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT
  30.  *    OF THIRD PARTY RIGHTS.
  31.  */
  32.  
  33. /*
  34. *    GraphicImport.m
  35. *
  36. *    This subclass of handles much of the overhead for imported
  37. *    files such as TIFF files and EPS files. Subclasses of this object
  38. *    provide the specific methods that differentiate the two.
  39. *
  40. *    Version:    2.0
  41. *    Author:    Ken Fromm
  42. *    History:
  43. *            03-20-91        Created the file.
  44. */
  45.  
  46. #import "GraphicImport.h"
  47. #import "DocView.h"
  48. #import "DrawingView.h"
  49. #import "DrawingViewWraps.h"
  50. #import "NXBitmapImageRepSub.h"
  51. #import "NXEPSImageRepSub.h"
  52. #import "ResourcePanel.h"
  53. #import "rotateprocs.h"
  54.  
  55. #import <appkit/Font.h>
  56. #import <appkit/NXImage.h>
  57. #import <appkit/Panel.h>
  58. #import <appkit/nextstd.h>
  59.  
  60. #import <objc/List.h>
  61. #import <dpsclient/wraps.h>
  62. #import <streams/streamsimpl.h>
  63.  
  64. #import <mach.h>
  65.  
  66. static const char    *ImageErrorString = "PostScript errors have been encountered in this file. The text of the errors can be found in the console output.";
  67.  
  68. @implementation GraphicImport
  69.  
  70. /*
  71.  *    Initializes a new GraphicImport object associated with the file.
  72.  *    If an EPS file, then check the resources. Three routes are possible.
  73.  *    The first makes everything as ok. The second cancels the import.
  74.  *    The third makes the file as unimageable.
  75.  */
  76. - initFromFile:(const char *) file
  77. {
  78.     char            *end;
  79.  
  80.     ResourceList        resourceList;
  81.  
  82.     self = [super  init];
  83.  
  84.     gflags.new = YES;
  85.     gflags.dirty = YES;
  86.     [self  setFilename:file];
  87.     image = [[NXImage  alloc]  init];
  88.     if (image)
  89.     {
  90.         end = strrchr(file, '.');
  91.         if (end)
  92.         {
  93.             if (strncmp(end, ".ps", 3) == 0 || strncmp(end, ".eps", 4) == 0)
  94.             {
  95.                 imagerep = [[NXEPSImageRepSub  alloc]  initFromFile:file];
  96.                 if (imagerep)
  97.                 {
  98.                     /*  Check the epsf file and list any that are unavailable. */
  99.                     if (![imagerep  checkResources:&resourceList] ||
  100.                         ![self  listUnavailableResources:&resourceList])
  101.                     {
  102.                         [imagerep  free];
  103.                         imagerep = NULL;
  104.                     }
  105.                     else
  106.                         [imagerep  getBoundingBox:&bounds];
  107.                 }
  108.                 else
  109.                     Notify("Import Error", "Unable to open file.");
  110.             }
  111.             else if (strncmp(end, ".tiff", 5) == 0)
  112.             {
  113.                 imagerep = [[NXBitmapImageRepSub  alloc]  initFromFile:file];
  114.                 if (imagerep)
  115.                 {
  116.                     bounds.origin.x = bounds.origin.y = 0.0;
  117.                     [imagerep  getSize:&bounds.size];
  118.                 }
  119.                 else
  120.                     Notify("Import Error", "Unable to open file.");
  121.             }
  122.         }
  123.     }
  124.     else
  125.         Notify("Import Error", "Unable to open file.");
  126.  
  127.     if (!imagerep)
  128.     {
  129.         [self  free];
  130.         self = nil;
  131.     }
  132.  
  133.     return self;
  134. }
  135.  
  136. /*
  137. *    Create a new object from a stream.
  138. */
  139. - initFromStream:(NXStream *) stream
  140. {
  141.     int            len, maxlen;
  142.  
  143.     char            *data;
  144.  
  145.     ResourceList        resourceList;
  146.  
  147.     self = [super  init];
  148.  
  149.     gflags.new = YES;
  150.     gflags.dirty = YES;
  151.     image = [[NXImage  alloc]  init];
  152.     if (image)
  153.     {
  154.         NXGetMemoryBuffer(stream, &data, &len, &maxlen);
  155.         if (strncmp(data, "%!PS-Adobe-", 11) == 0)
  156.         {
  157.             imagerep = [[NXEPSImageRepSub  alloc]  initFromStream:stream];
  158.             if (imagerep)
  159.             {
  160.                 /* Check the epsf file and list any that are unavailable. */
  161.                 if (![imagerep  checkResources:&resourceList] ||
  162.                     ![self  listUnavailableResources:&resourceList])
  163.                 {
  164.                     [imagerep  free];
  165.                     imagerep = NULL;
  166.                 }
  167.                 else
  168.                     [imagerep  getBoundingBox:&bounds];
  169.             }
  170.             else
  171.                 Notify("Import Error", "Unable to open file.");
  172.         }
  173.         else
  174.         {
  175.             imagerep = [[NXBitmapImageRepSub  alloc]  initFromStream:stream];
  176.             if (imagerep)
  177.             {
  178.                 bounds.origin.x = bounds.origin.y = 0.0;
  179.                 [imagerep  getSize:&bounds.size];
  180.             }
  181.             else
  182.                 Notify("Import Error", "Unable to open file.");
  183.         }
  184.     }
  185.     else
  186.         Notify("Import Error", "Unable to open file.");
  187.  
  188.     if (!imagerep)
  189.     {
  190.         [self  free];
  191.         self = nil;
  192.     }
  193.  
  194.     return self;
  195. }
  196.  
  197. - (BOOL) listUnavailableResources:(ResourceList *) resourceList;
  198. {
  199.     BOOL        unavailable;
  200.  
  201.     int            i, tag;
  202.  
  203.     tag = NX_OKTAG;
  204.     unavailable = NO;
  205.     for (i = 0; i < RES_NUMTYPES; i++)
  206.         unavailable = unavailable | (resourceList->types[i] != NULL);
  207.     
  208.     if (unavailable)
  209.     {
  210.         tag = [[NXApp  resourcePanel]  runModalWithList:resourceList  andName:filename];
  211.         if (tag == NX_CANCELTAG)
  212.         {
  213.             [imagerep  free];
  214.             imagerep = NULL;
  215.         }
  216.         else if (tag == NX_ALERTOTHER)
  217.             gflags.unimageable = YES;
  218.     }
  219.  
  220.     for (i = 0; i < RES_NUMTYPES; i++)
  221.         [resourceList->types[i]  free];
  222.  
  223.     return (tag != NX_CANCELTAG);
  224. }
  225.  
  226. /*
  227. *    Used when printing. Add the resources used in this file
  228. *    to the list passed in if the file is in the rectangle.
  229. */
  230. - addResources:(Resource *) resourceDoc  for:(NXRect *) r
  231. {
  232.     if (!r || IntersectsRotatedRect(r, &bounds, rotation))
  233.         [imagerep  addResources:resourceDoc  forFile:filename];
  234.     
  235.     return self;
  236. }
  237.  
  238. - free
  239. {
  240.     [image  free];
  241.     [imagerep  free];
  242.  
  243.     return [super  free];
  244. }
  245.  
  246. - freeTemp
  247. {
  248.     return [super  free];
  249. }
  250.  
  251. - copyTemp
  252. {
  253.     id    new;
  254.  
  255.     new = [super copy];
  256.     [new  setFilename:filename];
  257.     [new  setImage:image];
  258.     [new  setImageRep:imagerep];
  259.  
  260.     return new;
  261. }
  262.  
  263. - setFilename:(const char *) file
  264. {
  265.     if (file)
  266.         filename = NXUniqueString(file);
  267.  
  268.     return self;
  269. }
  270.  
  271. /* Used when producing a copy of this object. */
  272. - setImage:anImage
  273. {
  274.     image = anImage;
  275.  
  276.     return self;
  277. }
  278.  
  279. /* Used when producing a copy of this object. */
  280. - setImageRep:anImageRep
  281. {
  282.     imagerep = anImageRep;
  283.  
  284.     return self;
  285. }
  286.  
  287. - setSelected:(BOOL) flag
  288. {
  289.     gflags.selected = flag;
  290.  
  291.     return self;
  292. }
  293.  
  294. - (BOOL) selected
  295. {
  296.     return (BOOL) gflags.selected;
  297. }
  298.  
  299. - setImageable:(BOOL) flag
  300. {
  301.     gflags.unimageable = !flag;
  302.  
  303.     return self;
  304. }
  305.  
  306. - (BOOL) imageable
  307. {
  308.     return (BOOL) !gflags.unimageable;
  309. }
  310.  
  311. - (BOOL) error
  312. {
  313.     return gflags.error;
  314. }
  315.  
  316. /*
  317. *    pt_num is the changing control point. pt holds the relative change in each coordinate. 
  318. *    The relative is needed and not the absolute because the closest inside control point
  319. *    changes when one of the outside points change.
  320. */
  321. - setPoint:(int) pt_num  :(const NXPoint *) pt
  322. {    
  323.     int            row, col;
  324.  
  325.     NXPoint        aPt, cPt, oldOrigin;
  326.  
  327.     if (pt->x || pt->y)
  328.     {
  329.         gflags.dirty = YES;
  330.         aPt = *pt;
  331.         cPt.x = cPt.y = 0.0;
  332.         RotatePoint(&aPt, &cPt, -rotation);
  333.  
  334.         oldOrigin = bounds.origin;
  335.  
  336.         col = pt_num % 3;
  337.         switch (col)
  338.         {
  339.             case LEFT:
  340.                 bounds.origin.x += aPt.x;    
  341.                 bounds.size.width -= aPt.x;
  342.                 break;
  343.             case RIGHT:
  344.                 bounds.size.width += aPt.x;    
  345.                 break;
  346.         }
  347.  
  348.         row = pt_num/3;
  349.         switch (row)
  350.         {
  351.             case TOP:
  352.                 bounds.size.height += aPt.y;    
  353.                 break;
  354.             case BOTTOM:
  355.                 bounds.origin.y += aPt.y;
  356.                 bounds.size.height -= aPt.y;
  357.                 break;
  358.         }
  359.         RotatePoint(&bounds.origin, &oldOrigin, rotation);
  360.     }
  361.  
  362.     return self;
  363. }
  364.  
  365. - setSize:(const NXSize *) aSize
  366. {
  367.     if (bounds.size.width != aSize->width && bounds.size.height != aSize->height)
  368.     {
  369.         gflags.dirty = YES;
  370.         bounds.size = *aSize;
  371.     }
  372.  
  373.     return self;
  374. }
  375.  
  376. - setOrigin:(const NXPoint *) pt
  377. {    
  378.     bounds.origin = *pt;
  379.  
  380.     return self;
  381. }
  382.  
  383. - setBounds:(const NXRect *) aRect
  384. {
  385.     [self  setSize:&aRect->size];
  386.     [self  setOrigin:&aRect->origin];
  387.  
  388.     return self;
  389. }
  390.  
  391. /*
  392. *    Return the dimensions to the files original size.
  393. *    Keep the same upper left corner.
  394. */    
  395. - setOriginalSize
  396. {
  397.     NXSize        original;
  398.  
  399.     NXRect        boundsNew;
  400.  
  401.     [imagerep  getSize:&original];
  402.     if (bounds.size.width != original.width || bounds.size.height != original.height)
  403.     {
  404.         gflags.dirty = YES;
  405.         boundsNew.origin.x = bounds.origin.x;
  406.         boundsNew.origin.y = bounds.origin.y + bounds.size.height - original.height;    
  407.         boundsNew.size = original;
  408.         RotatePoint(&boundsNew.origin, &bounds.origin, rotation);
  409.         bounds = boundsNew;
  410.     }
  411.  
  412.     return self;
  413. }
  414.  
  415. /*
  416. *    Return the dimensions to the files original ratio.
  417. *    Use its mimimum dimension as the guide to figure
  418. *    the other dimension keeping the same upper left corner.
  419. */    
  420. - setOriginalRatio
  421. {
  422.     float            aspect, aspectOrig;
  423.  
  424.     NXSize        original;
  425.  
  426.     NXRect        boundsNew;
  427.  
  428.     [imagerep  getSize:&original];
  429.     aspect = bounds.size.width/bounds.size.height;
  430.     aspectOrig = original.width/original.height;
  431.     if (aspect != aspectOrig)
  432.     {
  433.         gflags.dirty = YES;
  434.         boundsNew.size = bounds.size;
  435.         if (aspect > aspectOrig)
  436.             boundsNew.size.width = original.width * (bounds.size.height/original.height);
  437.         else
  438.             boundsNew.size.height = original.height * (bounds.size.width/original.width);
  439.     
  440.         if (boundsNew.size.width < SIZE_MIN || boundsNew.size.height < SIZE_MIN)
  441.         {
  442.             if (aspectOrig > 1.0)
  443.             {
  444.                 boundsNew.size.width = SIZE_MIN * aspectOrig;
  445.                 boundsNew.size.height = SIZE_MIN;
  446.             }
  447.             else
  448.             {
  449.                 boundsNew.size.width = SIZE_MIN;
  450.                 boundsNew.size.height = SIZE_MIN/aspectOrig;
  451.             }
  452.         }
  453.  
  454.         boundsNew.origin.x = bounds.origin.x;
  455.         boundsNew.origin.y = bounds.origin.y + bounds.size.height - boundsNew.size.height;    
  456.         RotatePoint(&boundsNew.origin, &bounds.origin, rotation);
  457.  
  458.         bounds = boundsNew;
  459.  
  460.         return self;
  461.     }
  462.  
  463.     return nil;
  464. }
  465.  
  466. /*    Rotate the object about the point. */
  467. - rotateAboutPoint:(NXPoint *) aPoint  withAngle:(float) angle
  468. {
  469.     if (angle != 0.0)
  470.     {
  471.         gflags.dirty = YES;
  472.         rotation += angle;
  473.         RotatePoint(&bounds.origin, aPoint, angle);
  474.     }
  475.     return self;
  476. }
  477.  
  478. /* The pt argument holds the relative point change. */
  479. - moveAll:(const NXPoint *) pt
  480. {
  481.     bounds.origin.x += pt->x;
  482.     bounds.origin.y += pt->y;
  483.  
  484.     return self;
  485. }
  486.  
  487. /* Given the point number, return the point. */
  488. - getPoint:(int) pt_num  :(NXPoint *) pt
  489. {    
  490.     pt->x = bounds.origin.x + (pt_num % 3) * (bounds.size.width/2.0);
  491.     pt->y = bounds.origin.y + bounds.size.height - (pt_num/3) * (bounds.size.height/2.0);
  492.  
  493.     RotatePoint(pt, &bounds.origin, rotation);
  494.     
  495.     return self;
  496. }
  497.  
  498. /*
  499. *    Returns the bounding box of the file. Factors in the rotation.
  500. */
  501. - getBounds:(NXRect *)aRect
  502. {
  503.     RotateRectBounds(aRect, &bounds, &bounds.origin, rotation);
  504.  
  505.     return self;
  506. }
  507.  
  508. /*
  509. *    Return the rectangle that should be used for scrolling purposes.
  510. *    When the rectangle passes out of the visible rectangle then
  511. *    the screen should scroll.
  512. */
  513. - getScrollRect:(NXRect *)aRect  forPtNum:(int) pt_num
  514. {
  515.     if (pt_num == -1)
  516.     {
  517.         [self  getBounds:aRect];
  518.     }
  519.     else
  520.     {
  521.         [self  getPoint:pt_num  :&aRect->origin];
  522.         aRect->size.width = aRect->size.height = 0;
  523.     }
  524.  
  525.     return self;
  526. }
  527.  
  528. /*     Constrain to ANGLES if shift key is held down. */
  529. - constrainAngle:(float *)angle  withFlags:(int) flags
  530. {
  531.     if ((flags & NX_SHIFTMASK) == NX_SHIFTMASK)
  532.         *angle = rint((rotation + *angle) / M_PI * ANGLES) * M_PI / ANGLES - rotation;
  533.  
  534.     return self;
  535. }
  536.  
  537. /* 
  538. *    This method constains the point to the bounds of the view. The
  539. *    constaining is dependent on the control point that has been selected.
  540. */
  541. - constrainPoint:(NXPoint *)aPt  forPtNum:(int *) pt_num
  542.         inRect:(const NXRect *) viewRect  withFlags:(int) flags
  543. {
  544.     int            row, col,
  545.                 directionx, directiony;
  546.  
  547.     float            aspect_ratio;
  548.  
  549.     NXSize        proposed, original;
  550.  
  551.     NXPoint        oldOrigin;
  552.  
  553.     aPt->x = MAX(viewRect->origin.x, aPt->x);
  554.     aPt->x = MIN(viewRect->origin.x + viewRect->size.width, aPt->x);
  555.     aPt->y = MAX(viewRect->origin.y, aPt->y);        
  556.     aPt->y = MIN(viewRect->origin.y + viewRect->size.height, aPt->y);
  557.  
  558.     RotatePoint(aPt, &bounds.origin, -rotation);
  559.     oldOrigin = bounds.origin;
  560.     [imagerep  getSize:&original];
  561.  
  562.     col = *pt_num % 3;
  563.     row = *pt_num/3;
  564.     directionx = directiony = 0;
  565.     switch (col)
  566.     {
  567.         case LEFT:
  568.             proposed.width = bounds.origin.x + bounds.size.width - aPt->x;
  569.             directionx =1;    
  570.             break;        
  571.         case MIDDLE:
  572.             proposed.width = bounds.size.width;
  573.             aPt->x = bounds.origin.x + bounds.size.width/2.0;
  574.             break;
  575.         case RIGHT:
  576.             proposed.width = aPt->x - bounds.origin.x;
  577.             directionx = -1;                
  578.             break;
  579.     }
  580.  
  581.     switch (row)
  582.     {
  583.         case TOP:
  584.             proposed.height = aPt->y - bounds.origin.y;
  585.             directiony = -1;
  586.             break;            
  587.         case MIDDLE:
  588.             proposed.height = bounds.size.height;
  589.             aPt->y = bounds.origin.y + bounds.size.height/2.0;
  590.             break;
  591.         case BOTTOM:
  592.             proposed.height = bounds.origin.y + bounds.size.height - aPt->y;
  593.             directiony = 1;    
  594.             break;
  595.     }
  596.  
  597.     /* Constrain to the ratio of width vs. height. If new use the original ratio. */    
  598.     if ((flags & NX_SHIFTMASK) == NX_SHIFTMASK || gflags.new)
  599.     {
  600.         if (col != MIDDLE && row != MIDDLE)
  601.         {
  602.             if (gflags.new)
  603.                 aspect_ratio = original.width/original.height;
  604.             else
  605.                 aspect_ratio = bounds.size.width/bounds.size.height;
  606.             if (proposed.width < SIZE_MIN || proposed.height < SIZE_MIN)
  607.             {
  608.                 if (aspect_ratio > 1.0)
  609.                 {
  610.                     aPt->x = aPt->x + directionx * (proposed.width - SIZE_MIN * aspect_ratio);
  611.                     aPt->y = aPt->y + directiony * (proposed.height - SIZE_MIN);
  612.                 }
  613.                 else
  614.                 {
  615.                     aPt->x = aPt->x + directionx * (proposed.width - SIZE_MIN);
  616.                     aPt->y = aPt->y + directiony * (proposed.height - SIZE_MIN / aspect_ratio);
  617.                 }
  618.             }
  619.             else
  620.             {
  621.                 if (proposed.width/proposed.height > aspect_ratio)
  622.                     aPt->x = aPt->x + directionx *
  623.                         (proposed.width - proposed.height * aspect_ratio);
  624.                 else
  625.                     aPt->y = aPt->y + directiony *
  626.                         (proposed.height - proposed.width / aspect_ratio);
  627.             }
  628.         }
  629.         else
  630.         {
  631.             [self  getPoint:*pt_num :aPt];
  632.             RotatePoint(aPt, &bounds.origin, -rotation);
  633.         }
  634.     }
  635.     else
  636.     {
  637.         if (proposed.width < SIZE_MIN)
  638.             aPt->x = aPt->x + (proposed.width - SIZE_MIN) * directionx;
  639.         if (proposed.height < SIZE_MIN)
  640.             aPt->y = aPt->y + (proposed.height - SIZE_MIN) * directiony;
  641.     }
  642.  
  643.     RotatePoint(aPt, &bounds.origin, rotation);
  644.  
  645.     return self;
  646. }
  647.  
  648. /*
  649. *    Check for a control point hit. No need to perform the hit detection in
  650. *    the server since its a simple rectangle intersection check. Return the
  651. *    point number hit in the pt_num argument. The numbering starts in
  652. *    the lower left and goes left to right and bottom to top.
  653. */
  654. - hitControl:(const NXRect *) hitRect  :(int *) pt_num  forSize:(float) size
  655. {
  656.     int        i, j;
  657.  
  658.     float        halfc, halfw, halfh, halfhitw, halfhith;
  659.  
  660.     NXRect    knobRect, mouseRect;
  661.  
  662.     mouseRect = *hitRect;
  663.     if (rotation != 0.0)
  664.     {
  665.         halfhitw= mouseRect.size.width/2;
  666.         halfhith = mouseRect.size.width/2;
  667.         NXInsetRect(&mouseRect, halfhitw, halfhith);
  668.         RotatePoint(&mouseRect.origin, &bounds.origin, -rotation);
  669.         NXInsetRect(&mouseRect, -halfhitw, -halfhith);
  670.     };
  671.  
  672.     knobRect.size.width = knobRect.size.height = size;
  673.     halfc = knobRect.size.width/2;
  674.     halfw = bounds.size.width/2;
  675.     halfh = bounds.size.height/2;
  676.     for (i=0; i < 3; i ++)
  677.     {
  678.         for (j = 0; j < 3; j++)
  679.         {
  680.             if (!(i == MIDDLE && j == MIDDLE))
  681.             {
  682.                 knobRect.origin.x = bounds.origin.x +  j * halfw - halfc;
  683.                 knobRect.origin.y = bounds.origin.y + bounds.size.height - i * halfh - halfc;
  684.                 if (NXIntersectsRect(&mouseRect, &knobRect))
  685.                 {
  686.                     *pt_num = i * 3 + j;
  687.                     return self;
  688.                 }
  689.             }
  690.         }
  691.     }
  692.  
  693.     return nil;
  694. }
  695.  
  696. /*
  697. *    Check for hit dectection on the object. Since the hitpoint and the
  698. *    object is a rectangle just perform a rectangle intersection check.
  699. *    (The hit point is adjusted for rotation so that the intersection check
  700. *    can be used even on a rotated epsf file.) 
  701. */
  702. - hitObject:(UPath *) hitUpath  ifNotSelected:(BOOL) flag
  703. {
  704.     float        halfhitx, halfhity;
  705.  
  706.     NXRect    hitRect;
  707.  
  708.     if (!flag || !gflags.selected)
  709.     {    
  710.         NXSetRect(&hitRect, hitUpath->pts[0], hitUpath->pts[1],
  711.             hitUpath->pts[2] - hitUpath->pts[0],
  712.              hitUpath->pts[3] - hitUpath->pts[1]);
  713.  
  714.         if (rotation != 0.0)
  715.         {
  716.             halfhitx= hitRect.size.width/2;
  717.             halfhity = hitRect.size.width/2;
  718.             NXInsetRect(&hitRect, halfhitx, halfhity);
  719.             RotatePoint(&hitRect.origin, &bounds.origin, -rotation);
  720.             NXInsetRect(&hitRect, -halfhitx, -halfhity);
  721.         }
  722.  
  723.         if (NXIntersectsRect(&hitRect, &bounds))
  724.             return self;
  725.     }
  726.  
  727.     return nil;
  728. }
  729.  
  730. /*
  731. *    Place the point locations and the chararacters for the control
  732. *    points into the user path description passed in. In this case, the
  733. *    xyshow operator is used instead of a user path. But because the
  734. *    xyshow operator takes the same data format as the user path,
  735. *    the buffers for the user paths are used.  The position of the point
  736. *    is calculated relative to the position of the previous point. If
  737. *    this is the first set of points in the description then put the
  738. *    absolute values in place, otherwise use the lastPoint 
  739. *    argument to calculate the displacement.
  740. */
  741. - putControlPoints:(UPath *)buffer  forRect:(NXRect *)r  :(NXPoint *) lastPoint
  742. {
  743.     int            i, j;
  744.  
  745.     NXPoint        pt;
  746.  
  747.     NXRect        bRect;
  748.     
  749.     [self  getBounds:&bRect]; 
  750.     if (!r || NXIntersectsRect(r, &bRect))
  751.     {
  752.         i = buffer->num_ops;
  753.         for (j = 0; j < PTS_GRAPHIC; j++)
  754.             buffer->ops[i++] = 'a';
  755.         buffer->num_ops += PTS_GRAPHIC;
  756.  
  757.         i = buffer->num_pts;
  758.         if (i == 0)
  759.         {        
  760.             [self  getPoint:0  :&pt];
  761.             buffer->pts[i++] = pt.x;
  762.             buffer->pts[i++] = pt.y;
  763.             *lastPoint = pt;
  764.             j = 1;
  765.         }
  766.         else
  767.             j = 0;
  768.  
  769.          for ( ; j < PTS_GRAPHIC + 1; j ++)
  770.         {
  771.             if (j != 4)
  772.             {
  773.                 [self  getPoint:j  :&pt];
  774.                 buffer->pts[i++] = pt.x - lastPoint->x;
  775.                 buffer->pts[i++] = pt.y - lastPoint->y;
  776.                 *lastPoint = pt;
  777.             }
  778.         }
  779.  
  780.         buffer->num_pts += PTS_GRAPHIC*2;
  781.     }
  782.     
  783.     return self;
  784. }
  785.  
  786. /*
  787. *    Draw a box around the file. If imaging write "imaging file..." if not
  788. *    then draw cross hatches. Ideally we would like to draw the file
  789. *    name in the box but obtaining the font metrics for the font is a
  790. *    non-trivial exercise that is incorporated into this application.
  791. */
  792. - drawBoxforRect:(NXRect *) r  imaging:(BOOL) imageFlag
  793. {
  794.     char        *string;
  795.  
  796.     NXSize    strSize;
  797.  
  798.     PSgsave();
  799.     NXRectClip(r);
  800.     PSWTranslateRotate(bounds.origin.x, bounds.origin.y, rotation * ANGLE);
  801.     PSsetgray(NX_LTGRAY);
  802.     PSrectfill(0.0, 0.0, bounds.size.width, bounds.size.height);
  803.  
  804.     if (imageFlag)
  805.     {
  806.         strSize.width = 70;
  807.         strSize.height = 12;
  808.         string = "Imaging file...";
  809.     }
  810.     else
  811.     {
  812.         PSWSetLine(2.0, NX_DKGRAY);
  813.         PSWStrokeX(0.0, 0.0, bounds.size.width, bounds.size.height);
  814.         PSrectstroke(0.0, 0.0, bounds.size.width, bounds.size.height);
  815.         /* Insert code to show the file name. */
  816.         strSize.width = bounds.size.width;
  817.         strSize.height = bounds.size.height;
  818.         string = (char *) filename;
  819.     }
  820.  
  821.     if (string && bounds.size.width > strSize.width && bounds.size.height > strSize.height)
  822.     {
  823.         PSsetgray(NX_BLACK);
  824.         PSmoveto((bounds.size.width - strSize.width)/2, (bounds.size.height - strSize.height)/2);
  825.         PSselectfont("Helvetica",  12);
  826.         PSshow(string);
  827.     }
  828.     PSgrestore();
  829.  
  830.     return self;
  831. }
  832.  
  833. /*
  834.  *    Draws the TIFF or EPS file.  Sets up for drawing in a window if drawing
  835.  *    onscreen and the image is smaller than the maximum window size
  836.  *    permitted.  Makes the window transparent and scales it to the right
  837.  *    size to match the scale of the document.
  838.  */
  839. - drawObject:(NXRect *) r  withFlags:(int) flags  inView:view
  840. {
  841.     BOOL        useImage;
  842.  
  843.     float            scale;
  844.  
  845.     NXRect        rect, frame;
  846.  
  847.     if (!r || IntersectsRotatedRect(r, &bounds, rotation))
  848.     {
  849.         if (flags & REDRAWFLAG)
  850.         {
  851.             scale = [[view  superview]  scale];
  852.             PSgsave();
  853.             PSWTranslateRotate(bounds.origin.x, bounds.origin.y, rotation * ANGLE);
  854.             PSWSetLine(1.0/scale, NX_LTGRAY);
  855.             PSrectstroke(0.0, 0.0, bounds.size.width, bounds.size.height);
  856.             PSgrestore();
  857.         }
  858.         else if (gflags.unimageable || ![NXApp  imagingFlag])
  859.         {
  860.             [self  drawBoxforRect:r  imaging:NO];
  861.             gflags.new = NO;
  862.         }
  863.         else if (NXDrawingStatus == NX_PRINTING ||
  864.                 NXDrawingStatus == NX_COPYING ||
  865.                     [view  image])
  866.         {
  867.             [imagerep  drawIn:&bounds  with:(rotation*180/M_PI)];
  868.         }
  869.         else
  870.         {
  871.             useImage = YES;
  872.  
  873.             /* Get the frame of the epsf file. */
  874.             RotateRectBounds(&frame, &bounds, &bounds.origin, rotation);
  875.             scale = [[view  superview]  scale];
  876.     
  877.             /*
  878.             *    Size the cache. Use the NXImage if the size is less than
  879.             *    a predetermined size.
  880.             */
  881.             [image  getSize:&rect.size];
  882.             if (frame.size.width*scale != rect.size.width ||
  883.                 frame.size.height*scale != rect.size.height)
  884.             {
  885.                 gflags.dirty = YES;
  886.                 rect.size.width = frame.size.width * scale;
  887.                 rect.size.height = frame.size.height * scale;
  888.                 if (rect.size.width < IMAGE_MAX && rect.size.height < IMAGE_MAX)
  889.                 {
  890.                     [image  setSize:&rect.size];
  891.                     [image  getSize:&rect.size];
  892.                 }
  893.                 else
  894.                     useImage = NO;
  895.             }
  896.                 
  897.             if (gflags.dirty)
  898.             {
  899.                 /*
  900.                 *    Draw the gray box that tells the user
  901.                 *    the file is being imaged.
  902.                 */
  903.                 [view  lockFocus];
  904.                 [self  drawBoxforRect:r  imaging:YES];
  905.                 [view  unlockFocus];
  906.                 [[view  window]  flushWindow];
  907.                 NXPing();
  908.  
  909.                 /*
  910.                 *    Setup the drawing so that it goes into an NXImage.
  911.                 *    Some prep work needs to be done to set the
  912.                 *    position.
  913.                 */
  914.                 if (useImage)
  915.                 {
  916.                     rect.origin.x = rect.origin.y = 0;
  917.                     [image  lockFocus];
  918.                     PSsetalpha(0.0);
  919.                     PSsetgray(NX_WHITE);
  920.                     NXRectFill(&rect);
  921.                     PSsetalpha(1.0);
  922.  
  923.                     PSscale(scale, scale);
  924.                     PStranslate(-frame.origin.x, -frame.origin.y);
  925.                 }
  926.  
  927.                 if ([NXApp  tracingFlag])
  928.                     DPSTraceContext(DPSGetCurrentContext(), YES);
  929.  
  930.                 /*
  931.                 *    Draw the file into a subclass of NXEPSImageRep.
  932.                 *    Take the rotation into account.
  933.                 */
  934.                 gflags.error = ![imagerep  drawIn:&bounds  with:(rotation*180/M_PI)];
  935.  
  936.                 if ([NXApp  tracingFlag])
  937.                     DPSTraceContext(DPSGetCurrentContext(), NO);
  938.  
  939.                 if (useImage)
  940.                     [image  unlockFocus];
  941.  
  942.                 /*
  943.                 *    Display an error panel if one occurs. If it is
  944.                 *    a newly imported file, it will not be
  945.                 *    imported. If it is an existing file,
  946.                 *    will be marked as unimageable.
  947.                 */
  948.                 if (gflags.error)
  949.                 {
  950.                     gflags.unimageable = YES;
  951.                     [self  displayError];
  952.                     if (!gflags.new)
  953.                         [self  drawBoxforRect:r  imaging:NO];
  954.                 }
  955.             }
  956.  
  957.             /*
  958.             *    Composite the buffer to the currently focused view.
  959.             */
  960.             if (useImage && !gflags.error)
  961.             {
  962.                 rect.origin.x = rect.origin.y = 0;
  963.                 rect.size.width = frame.size.width * scale;
  964.                 rect.size.height = frame.size.height * scale;
  965.                 [image  composite:NX_SOVER  fromRect:&rect  toPoint:&frame.origin];
  966.             }
  967.  
  968.             gflags.new = NO;
  969.             gflags.dirty = NO;
  970.         }
  971.     }
  972.  
  973.     return self;
  974. }
  975.  
  976. /*
  977. *    Write out the bounds, rotation, filename and imagerep.
  978. */
  979. - write:(NXTypedStream *)stream
  980. {
  981.     [super write:stream];
  982.  
  983.     NXWriteRect(stream, &bounds);
  984.     NXWriteType(stream, "f", &rotation);
  985.     NXWriteType(stream, "%", &filename);
  986.     NXWriteObject(stream, imagerep);
  987.         
  988.     return self;
  989. }
  990.  
  991. /*
  992. *    Read the bounds, rotation, filename and imagerep.
  993. */
  994. - read:(NXTypedStream *)stream
  995. {
  996.     [super read:stream];
  997.  
  998.     NXReadRect(stream, &bounds);
  999.     NXReadType(stream, "f", &rotation);
  1000.     NXReadType(stream, "%", &filename);
  1001.     imagerep = NXReadObject(stream);
  1002.  
  1003.     return self;
  1004. }
  1005.  
  1006. - awake
  1007. {
  1008.     gflags.new = YES;
  1009.     gflags.dirty = YES;
  1010.     image = [[NXImage  alloc]  init];
  1011.  
  1012.     return self;
  1013. }
  1014.  
  1015. - displayError
  1016. {
  1017.     NXRunAlertPanel("Imaging Error", ImageErrorString, "OK", NULL, NULL);
  1018.  
  1019.     return self;
  1020. }
  1021.  
  1022. @end
  1023.